#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <queue>
using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c_cond = PTHREAD_COND_INITIALIZER;//其实现在这种场景放在一个队列与两个队列中没有什么区别
pthread_cond_t p_cond = PTHREAD_COND_INITIALIZER;//我认为应该是在之后生产者之间或者是消费者之间
                                                 //他们两大类自己内部的互斥需要cond控制顺序，所以这里有两个条件变量

template <class T>
class dataQueue
{
public:
    void push(const T &data)
    {
        pthread_mutex_lock(&mutex);
        if (_q.size() >= _size)
        {
            pthread_cond_signal(&c_cond);//唤醒消费者
            pthread_cond_wait(&p_cond, &mutex);
        }
        _q.push(data);
        cout << "comsumer push " << data << " to queue" << endl;
        // if(_q.size()>=push_max)//我认为这里的代码是没有必要的，下面同样的地方有解释
        // {
        //     pthread_cond_signal(&c_cond);//唤醒消费者
        // }
        pthread_mutex_unlock(&mutex);
    }

    void pop()
    {
        pthread_mutex_lock(&mutex);
        if (_q.size() == 0)
        {
            pthread_cond_signal(&p_cond);//唤醒生产者
            pthread_cond_wait(&c_cond, &mutex);
        }
        T top = _q.front();
        _q.pop();
        cout << "productor pop " << top << " from queue" << endl;
        // if(_q.size()<=pop_min)//这是蛋哥的代码，我认为这里的控制似乎是没有任何用的，当队列中数据小于了最小pop数时唤醒生产者
        // {                     //而生产者只会在锁被释放时才会被唤醒，而我们自己这个循环会马上去获取锁,而队列中的数据此时是为
                                 //pop_min的，除非我们将等待条件设置为pop_min否则消费者不会停止动作，而如果把等待条件设置为pop_min
                                 //那我们就根本没有必要用两个循环来控制，之间将唤醒与等待放入同一个循环即可，所以我认为这里的代码是没有必要的

        //     pthread_cond_signal(&p_cond);//唤醒生产者
        // }
        pthread_mutex_unlock(&mutex);
    }

private:
    queue<T> _q;
    int _size =5;//这是我们控制的一个队列中最多可以容纳的数据量

    //我们可以通过这些变量来控制pop与push的动作
    // int push_max=8;//当队列中数据压入最多个数
    // int pop_min=3;//队列中数据存在最少个数
};

dataQueue<int> q;

struct threadData
{
    string _threadName;
    threadData(string name)
        : _threadName(name)
    {
    }
    threadData() = default;
};

void *productor(void *args)
{
    threadData *data = static_cast<threadData *>(args);
    int i = 0;
    while (true)
    {
        sleep(1);
        q.push(i++);
    }
}

void *consumer(void *args)
{
    threadData *data = static_cast<threadData *>(args);
    while (true)
    {
        q.pop();
    }
}

int main()
{
    pthread_t ctid, ptid;
    threadData *data1 = new threadData("comsumer");
    threadData *data2 = new threadData("productor");
    pthread_create(&ctid, nullptr, consumer, data1);
    pthread_create(&ptid, nullptr, productor, data2);

    void *retData;
    pthread_join(ctid, &retData);
    pthread_join(ptid, &retData);

    return 0;
}